home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkScrollbar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-22  |  35.8 KB  |  1,191 lines

  1. /* 
  2.  * tkScrollbar.c --
  3.  *
  4.  *    This module implements a scrollbar widgets for the Tk
  5.  *    toolkit.  A scrollbar displays a slider and two arrows;
  6.  *    mouse clicks on features within the scrollbar cause
  7.  *    scrolling commands to be invoked.
  8.  *
  9.  * Copyright 1990-1992 Regents of the University of California.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkScrollbar.c,v 1.35 92/05/22 16:57:27 ouster Exp $ SPRITE (Berkeley)";
  21. #endif
  22.  
  23. #include "tkConfig.h"
  24. #include "default.h"
  25. #include "tkInt.h"
  26.  
  27. /*
  28.  * A data structure of the following type is kept for each scrollbar
  29.  * widget managed by this file:
  30.  */
  31.  
  32. typedef struct {
  33.     Tk_Window tkwin;        /* Window that embodies the scrollbar.  NULL
  34.                  * means that the window has been destroyed
  35.                  * but the data structures haven't yet been
  36.                  * cleaned up.*/
  37.     Tcl_Interp *interp;        /* Interpreter associated with scrollbar. */
  38.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  39.                  * "horizontal"). */
  40.     int vertical;        /* Non-zero means vertical orientation
  41.                  * requested, zero means horizontal. */
  42.     int width;            /* Desired narrow dimension of scrollbar,
  43.                  * in pixels. */
  44.     char *command;        /* Command prefix to use when invoking
  45.                  * scrolling commands.  NULL means don't
  46.                  * invoke commands.  Malloc'ed. */
  47.     int commandSize;        /* Number of non-NULL bytes in command. */
  48.     int repeatDelay;        /* How long to wait before auto-repeating
  49.                  * on scrolling actions (in ms). */
  50.     int repeatInterval;        /* Interval between autorepeats (in ms). */
  51.  
  52.     /*
  53.      * Information used when displaying widget:
  54.      */
  55.  
  56.     int borderWidth;        /* Width of 3-D borders. */
  57.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  58.     Tk_3DBorder fgBorder;    /* For drawing foreground shapes. */
  59.     Tk_3DBorder activeBorder;    /* For drawing foreground shapes when
  60.                  * active (i.e. when mouse is positioned
  61.                  * over element).  NULL means use fgBorder. */
  62.     GC copyGC;            /* Used for copying from pixmap onto screen. */
  63.     int relief;            /* Indicates whether window as a whole is
  64.                  * raised, sunken, or flat. */
  65.     int offset;            /* Zero if relief is TK_RELIEF_FLAT,
  66.                  * borderWidth otherwise.   Indicates how
  67.                  * much interior stuff must be offset from
  68.                  * outside edges to leave room for border. */
  69.     int arrowLength;        /* Length of arrows along long dimension of
  70.                  * scrollbar.  Recomputed on window size
  71.                  * changes. */
  72.     int sliderFirst;        /* Pixel coordinate of top or left edge
  73.                  * of slider area, including border. */
  74.     int sliderLast;        /* Coordinate of pixel just after bottom
  75.                  * or right edge of slider area, including
  76.                  * border. */
  77.     int mouseField;        /* Indicates which scrollbar element is
  78.                  * under mouse (e.g. TOP_ARROW;  see below
  79.                  * for possible values). */
  80.     int pressField;        /* Field in which button was pressed, or -1
  81.                  * if no button is down. */
  82.     int pressPos;        /* Position of mouse when button was
  83.                  * pressed (y for vertical scrollbar, x
  84.                  * for horizontal). */
  85.     int pressFirstUnit;        /* Value of "firstUnit" when mouse button
  86.                  * was pressed. */
  87.  
  88.     /*
  89.      * Information describing the application related to the scrollbar.
  90.      * This information is provided by the application by invoking the
  91.      * "set" widget command.
  92.      */
  93.  
  94.     int totalUnits;        /* Total dimension of application, in
  95.                  * units. */
  96.     int windowUnits;        /* Maximum number of units that can
  97.                  * be displayed in the window at
  98.                  * once. */
  99.     int firstUnit;        /* Number of last unit visible in
  100.                  * application's window. */
  101.     int lastUnit;        /* Index of last unit visible in window. */
  102.  
  103.     /*
  104.      * Miscellaneous information:
  105.      */
  106.  
  107.     Cursor cursor;        /* Current cursor for window, or None. */
  108.     Tk_TimerToken autoRepeat;    /* Token for auto-repeat that's
  109.                  * currently in progress.  NULL means no
  110.                  * auto-repeat in progress. */
  111.     int flags;            /* Various flags;  see below for
  112.                  * definitions. */
  113. } Scrollbar;
  114.  
  115. /*
  116.  * Legal values for "mouseField" field of Scrollbar structures.  These
  117.  * are also the return values from the ScrollbarPosition procedure.
  118.  */
  119.  
  120. #define TOP_ARROW    1
  121. #define TOP_GAP        2
  122. #define SLIDER        3
  123. #define BOTTOM_GAP    4
  124. #define BOTTOM_ARROW    5
  125. #define OUTSIDE        6
  126.  
  127. /*
  128.  * Flag bits for scrollbars:
  129.  * 
  130.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  131.  *                has already been queued to redraw
  132.  *                this window.
  133.  */
  134.  
  135. #define REDRAW_PENDING        1
  136.  
  137. /*
  138.  * Information used for argv parsing.
  139.  */
  140.  
  141.  
  142. static Tk_ConfigSpec configSpecs[] = {
  143.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  144.     DEF_SCROLLBAR_ACTIVE_FG_COLOR, Tk_Offset(Scrollbar, activeBorder),
  145.     TK_CONFIG_COLOR_ONLY},
  146.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  147.     DEF_SCROLLBAR_ACTIVE_FG_MONO, Tk_Offset(Scrollbar, activeBorder),
  148.     TK_CONFIG_MONO_ONLY},
  149.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  150.     DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder),
  151.     TK_CONFIG_COLOR_ONLY},
  152.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  153.     DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
  154.     TK_CONFIG_MONO_ONLY},
  155.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  156.     (char *) NULL, 0, 0},
  157.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  158.     (char *) NULL, 0, 0},
  159.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  160.     DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
  161.     {TK_CONFIG_STRING, "-command", "command", "Command",
  162.     DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command), 0},
  163.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  164.     DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
  165.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  166.     (char *) NULL, 0, 0},
  167.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  168.     DEF_SCROLLBAR_FG_COLOR, Tk_Offset(Scrollbar, fgBorder),
  169.     TK_CONFIG_COLOR_ONLY},
  170.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  171.     DEF_SCROLLBAR_FG_MONO, Tk_Offset(Scrollbar, fgBorder),
  172.     TK_CONFIG_MONO_ONLY},
  173.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  174.     DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
  175.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  176.     DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
  177.     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  178.     DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
  179.     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  180.     DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
  181.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  182.     DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
  183.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  184.     (char *) NULL, 0, 0}
  185. };
  186.  
  187. /*
  188.  * Forward declarations for procedures defined later in this file:
  189.  */
  190.  
  191. static void        ComputeScrollbarGeometry _ANSI_ARGS_((
  192.                 Scrollbar *scrollPtr));
  193. static int        ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
  194.                 Scrollbar *scrollPtr, int argc, char **argv,
  195.                 int flags));
  196. static void        DestroyScrollbar _ANSI_ARGS_((ClientData clientData));
  197. static void        DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
  198. static void        EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
  199. static void        ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
  200.                 XEvent *eventPtr));
  201. static void        ScrollbarMouseProc _ANSI_ARGS_((ClientData clientData,
  202.                 XEvent *eventPtr));
  203. static void        ScrollbarNewField _ANSI_ARGS_((Scrollbar *scrollPtr,
  204.                 int field));
  205. static int        ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
  206.                 int x, int y));
  207. static void        ScrollbarTimerProc _ANSI_ARGS_((
  208.                 ClientData clientData));
  209. static int        ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
  210.                 Tcl_Interp *, int argc, char **argv));
  211. static void        ScrollCmd _ANSI_ARGS_((Scrollbar *scrollPtr,
  212.                 int unit));
  213.  
  214. /*
  215.  *--------------------------------------------------------------
  216.  *
  217.  * Tk_ScrollbarCmd --
  218.  *
  219.  *    This procedure is invoked to process the "scrollbar" Tcl
  220.  *    command.  See the user documentation for details on what
  221.  *    it does.
  222.  *
  223.  * Results:
  224.  *    A standard Tcl result.
  225.  *
  226.  * Side effects:
  227.  *    See the user documentation.
  228.  *
  229.  *--------------------------------------------------------------
  230.  */
  231.  
  232. int
  233. Tk_ScrollbarCmd(clientData, interp, argc, argv)
  234.     ClientData clientData;    /* Main window associated with
  235.                  * interpreter. */
  236.     Tcl_Interp *interp;        /* Current interpreter. */
  237.     int argc;            /* Number of arguments. */
  238.     char **argv;        /* Argument strings. */
  239. {
  240.     Tk_Window tkwin = (Tk_Window) clientData;
  241.     register Scrollbar *scrollPtr;
  242.     Tk_Window new;
  243.  
  244.     if (argc < 2) {
  245.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  246.         argv[0], " pathName ?options?\"", (char *) NULL);
  247.     return TCL_ERROR;
  248.     }
  249.  
  250.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  251.     if (new == NULL) {
  252.     return TCL_ERROR;
  253.     }
  254.  
  255.     /*
  256.      * Initialize fields that won't be initialized by ConfigureScrollbar,
  257.      * or which ConfigureScrollbar expects to have reasonable values
  258.      * (e.g. resource pointers).
  259.      */
  260.  
  261.     scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar));
  262.     scrollPtr->tkwin = new;
  263.     scrollPtr->interp = interp;
  264.     scrollPtr->command = NULL;
  265.     scrollPtr->bgBorder = NULL;
  266.     scrollPtr->fgBorder = NULL;
  267.     scrollPtr->activeBorder = NULL;
  268.     scrollPtr->copyGC = None;
  269.     scrollPtr->mouseField = OUTSIDE;
  270.     scrollPtr->pressField = -1;
  271.     scrollPtr->totalUnits = 0;
  272.     scrollPtr->windowUnits = 0;
  273.     scrollPtr->firstUnit = 0;
  274.     scrollPtr->lastUnit = 0;
  275.     scrollPtr->cursor = None;
  276.     scrollPtr->autoRepeat = NULL;
  277.     scrollPtr->flags = 0;
  278.  
  279.     Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
  280.     Tk_CreateEventHandler(scrollPtr->tkwin, ExposureMask|StructureNotifyMask,
  281.         ScrollbarEventProc, (ClientData) scrollPtr);
  282.     Tk_CreateEventHandler(scrollPtr->tkwin, EnterWindowMask|LeaveWindowMask
  283.         |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
  284.         ScrollbarMouseProc, (ClientData) scrollPtr);
  285.     Tcl_CreateCommand(interp, Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
  286.         (ClientData) scrollPtr, (void (*)()) NULL);
  287.     if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
  288.     goto error;
  289.     }
  290.  
  291.     interp->result = Tk_PathName(scrollPtr->tkwin);
  292.     return TCL_OK;
  293.  
  294.     error:
  295.     Tk_DestroyWindow(scrollPtr->tkwin);
  296.     return TCL_ERROR;
  297. }
  298.  
  299. /*
  300.  *--------------------------------------------------------------
  301.  *
  302.  * ScrollbarWidgetCmd --
  303.  *
  304.  *    This procedure is invoked to process the Tcl command
  305.  *    that corresponds to a widget managed by this module.
  306.  *    See the user documentation for details on what it does.
  307.  *
  308.  * Results:
  309.  *    A standard Tcl result.
  310.  *
  311.  * Side effects:
  312.  *    See the user documentation.
  313.  *
  314.  *--------------------------------------------------------------
  315.  */
  316.  
  317. static int
  318. ScrollbarWidgetCmd(clientData, interp, argc, argv)
  319.     ClientData clientData;    /* Information about scrollbar
  320.                      * widget. */
  321.     Tcl_Interp *interp;            /* Current interpreter. */
  322.     int argc;                /* Number of arguments. */
  323.     char **argv;            /* Argument strings. */
  324. {
  325.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  326.     int result = TCL_OK;
  327.     int length;
  328.     char c;
  329.  
  330.     if (argc < 2) {
  331.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  332.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  333.     return TCL_ERROR;
  334.     }
  335.     Tk_Preserve((ClientData) scrollPtr);
  336.     c = argv[1][0];
  337.     length = strlen(argv[1]);
  338.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  339.     if (argc == 2) {
  340.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  341.             (char *) scrollPtr, (char *) NULL, 0);
  342.     } else if (argc == 3) {
  343.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  344.             (char *) scrollPtr, argv[2], 0);
  345.     } else {
  346.         result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
  347.             TK_CONFIG_ARGV_ONLY);
  348.     }
  349.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  350.     if (argc != 2) {
  351.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  352.             argv[0], " get\"", (char *) NULL);
  353.         goto error;
  354.     }
  355.     sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits,
  356.         scrollPtr->windowUnits, scrollPtr->firstUnit,
  357.         scrollPtr->lastUnit);
  358.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  359.     int totalUnits, windowUnits, firstUnit, lastUnit;
  360.  
  361.     if (argc != 6) {
  362.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  363.             argv[0],
  364.             " set totalUnits windowUnits firstUnit lastUnit\"",
  365.             (char *) NULL);
  366.         goto error;
  367.     }
  368.     if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
  369.         goto error;
  370.     }
  371.     if (totalUnits < 0) {
  372.         sprintf(interp->result, "illegal totalUnits %d", totalUnits);
  373.         goto error;
  374.     }
  375.     if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
  376.         goto error;
  377.     }
  378.     if (windowUnits < 0) {
  379.         sprintf(interp->result, "illegal windowUnits %d", windowUnits);
  380.         goto error;
  381.     }
  382.     if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
  383.         goto error;
  384.     }
  385.     if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
  386.         goto error;
  387.     }
  388.     if (totalUnits > 0) {
  389.         if (lastUnit < firstUnit) {
  390.         sprintf(interp->result, "illegal lastUnit %d", lastUnit);
  391.         goto error;
  392.         }
  393.     } else {
  394.         firstUnit = lastUnit = 0;
  395.     }
  396.     scrollPtr->totalUnits = totalUnits;
  397.     scrollPtr->windowUnits = windowUnits;
  398.     scrollPtr->firstUnit = firstUnit;
  399.     scrollPtr->lastUnit = lastUnit;
  400.     ComputeScrollbarGeometry(scrollPtr);
  401.     EventuallyRedraw(scrollPtr);
  402.     } else {
  403.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  404.         "\":  must be configure, get, or set", (char *) NULL);
  405.     goto error;
  406.     }
  407.     Tk_Release((ClientData) scrollPtr);
  408.     return result;
  409.  
  410.     error:
  411.     Tk_Release((ClientData) scrollPtr);
  412.     return TCL_ERROR;
  413. }
  414.  
  415. /*
  416.  *----------------------------------------------------------------------
  417.  *
  418.  * DestroyScrollbar --
  419.  *
  420.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  421.  *    to clean up the internal structure of a scrollbar at a safe time
  422.  *    (when no-one is using it anymore).
  423.  *
  424.  * Results:
  425.  *    None.
  426.  *
  427.  * Side effects:
  428.  *    Everything associated with the scrollbar is freed up.
  429.  *
  430.  *----------------------------------------------------------------------
  431.  */
  432.  
  433. static void
  434. DestroyScrollbar(clientData)
  435.     ClientData clientData;    /* Info about scrollbar widget. */
  436. {
  437.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  438.  
  439.     if (scrollPtr->command != NULL) {
  440.     ckfree(scrollPtr->command);
  441.     }
  442.     if (scrollPtr->bgBorder != NULL) {
  443.     Tk_Free3DBorder(scrollPtr->bgBorder);
  444.     }
  445.     if (scrollPtr->fgBorder != NULL) {
  446.     Tk_Free3DBorder(scrollPtr->fgBorder);
  447.     }
  448.     if (scrollPtr->activeBorder != NULL) {
  449.     Tk_Free3DBorder(scrollPtr->activeBorder);
  450.     }
  451.     if (scrollPtr->copyGC != None) {
  452.     Tk_FreeGC(scrollPtr->copyGC);
  453.     }
  454.     if (scrollPtr->cursor != None) {
  455.     Tk_FreeCursor(scrollPtr->cursor);
  456.     }
  457.     ckfree((char *) scrollPtr);
  458. }
  459.  
  460. /*
  461.  *----------------------------------------------------------------------
  462.  *
  463.  * ConfigureScrollbar --
  464.  *
  465.  *    This procedure is called to process an argv/argc list, plus
  466.  *    the Tk option database, in order to configure (or
  467.  *    reconfigure) a scrollbar widget.
  468.  *
  469.  * Results:
  470.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  471.  *    returned, then interp->result contains an error message.
  472.  *
  473.  * Side effects:
  474.  *    Configuration information, such as colors, border width,
  475.  *    etc. get set for scrollPtr;  old resources get freed,
  476.  *    if there were any.
  477.  *
  478.  *----------------------------------------------------------------------
  479.  */
  480.  
  481. static int
  482. ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
  483.     Tcl_Interp *interp;            /* Used for error reporting. */
  484.     register Scrollbar *scrollPtr;    /* Information about widget;  may or
  485.                      * may not already have values for
  486.                      * some fields. */
  487.     int argc;                /* Number of valid entries in argv. */
  488.     char **argv;            /* Arguments. */
  489.     int flags;                /* Flags to pass to
  490.                      * Tk_ConfigureWidget. */
  491. {
  492.     int length;
  493.     XGCValues gcValues;
  494.  
  495.     if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
  496.         argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
  497.     return TCL_ERROR;
  498.     }
  499.  
  500.     /*
  501.      * A few options need special processing, such as parsing the
  502.      * orientation or setting the background from a 3-D border.
  503.      */
  504.  
  505.     length = strlen(scrollPtr->orientUid);
  506.     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
  507.     scrollPtr->vertical = 1;
  508.     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
  509.     scrollPtr->vertical = 0;
  510.     } else {
  511.     Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
  512.         "\": must be vertical or horizontal", (char *) NULL);
  513.     return TCL_ERROR;
  514.     }
  515.  
  516.     if (scrollPtr->command != NULL) {
  517.     scrollPtr->commandSize = strlen(scrollPtr->command);
  518.     } else {
  519.     scrollPtr->commandSize = 0;
  520.     }
  521.  
  522.     Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
  523.  
  524.     if (scrollPtr->copyGC == None) {
  525.     gcValues.graphics_exposures = False;
  526.     scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
  527.         &gcValues);
  528.     }
  529.  
  530.     /*
  531.      * Register the desired geometry for the window (leave enough space
  532.      * for the two arrows plus a minimum-size slider, plus border around
  533.      * the whole window, if any).  Then arrange for the window to be
  534.      * redisplayed.
  535.      */
  536.  
  537.     ComputeScrollbarGeometry(scrollPtr);
  538.     EventuallyRedraw(scrollPtr);
  539.     return TCL_OK;
  540. }
  541.  
  542. /*
  543.  *--------------------------------------------------------------
  544.  *
  545.  * DisplayScrollbar --
  546.  *
  547.  *    This procedure redraws the contents of a scrollbar window.
  548.  *    It is invoked as a do-when-idle handler, so it only runs
  549.  *    when there's nothing else for the application to do.
  550.  *
  551.  * Results:
  552.  *    None.
  553.  *
  554.  * Side effects:
  555.  *    Information appears on the screen.
  556.  *
  557.  *--------------------------------------------------------------
  558.  */
  559.  
  560. static void
  561. DisplayScrollbar(clientData)
  562.     ClientData clientData;    /* Information about window. */
  563. {
  564.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  565.     register Tk_Window tkwin = scrollPtr->tkwin;
  566.     XPoint points[7];
  567.     Tk_3DBorder border;
  568.     int relief, width, fieldLength;
  569.     Pixmap pixmap;
  570.  
  571.     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  572.     goto done;
  573.     }
  574.  
  575.     if (scrollPtr->vertical) {
  576.     width = Tk_Width(tkwin) - 2*scrollPtr->offset;
  577.     } else {
  578.     width = Tk_Height(tkwin) - 2*scrollPtr->offset;
  579.     }
  580.  
  581.     /*
  582.      * In order to avoid screen flashes, this procedure redraws
  583.      * the scrollbar in a pixmap, then copies the pixmap to the
  584.      * screen in a single operation.  This means that there's no
  585.      * point in time where the on-sreen image has been cleared.
  586.      */
  587.  
  588.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  589.         Tk_Width(tkwin), Tk_Height(tkwin),
  590.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  591.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, scrollPtr->bgBorder,
  592.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  593.         scrollPtr->borderWidth, scrollPtr->relief);
  594.  
  595.     /*
  596.      * Draw the top or left arrow.  The coordinates of the polygon
  597.      * points probably seem odd, but they were carefully chosen with
  598.      * respect to X's rules for filling polygons.  These point choices
  599.      * cause the arrows to just fill the narrow dimension of the
  600.      * scrollbar and be properly centered.
  601.      */
  602.  
  603.     if (scrollPtr->mouseField == TOP_ARROW) {
  604.     border = scrollPtr->activeBorder;
  605.     relief = scrollPtr->pressField == TOP_ARROW ? TK_RELIEF_SUNKEN
  606.         : TK_RELIEF_RAISED;
  607.     } else {
  608.     border = scrollPtr->fgBorder;
  609.     relief = TK_RELIEF_RAISED;
  610.     }
  611.     if (scrollPtr->vertical) {
  612.     points[0].x = scrollPtr->offset - 1;
  613.     points[0].y = scrollPtr->arrowLength + scrollPtr->offset;
  614.     points[1].x = width + scrollPtr->offset;
  615.     points[1].y = points[0].y;
  616.     points[2].x = width/2 + scrollPtr->offset;
  617.     points[2].y = scrollPtr->offset - 1;
  618.     Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
  619.         points, 3, scrollPtr->borderWidth, relief);
  620.     } else {
  621.     points[0].x = scrollPtr->arrowLength + scrollPtr->offset;
  622.     points[0].y = scrollPtr->offset - 1;
  623.     points[1].x = scrollPtr->offset;
  624.     points[1].y = width/2 + scrollPtr->offset;
  625.     points[2].x = points[0].x;
  626.     points[2].y = width + scrollPtr->offset;
  627.     Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
  628.         points, 3, scrollPtr->borderWidth, relief);
  629.     }
  630.  
  631.     /*
  632.      * Display the bottom or right arrow.
  633.      */
  634.  
  635.     if (scrollPtr->mouseField == BOTTOM_ARROW) {
  636.     border = scrollPtr->activeBorder;
  637.     relief = scrollPtr->pressField == BOTTOM_ARROW ? TK_RELIEF_SUNKEN
  638.         : TK_RELIEF_RAISED;
  639.     } else {
  640.     border = scrollPtr->fgBorder;
  641.     relief = TK_RELIEF_RAISED;
  642.     }
  643.     if (scrollPtr->vertical) {
  644.     points[0].x = scrollPtr->offset;
  645.     points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
  646.         - scrollPtr->offset;
  647.     points[1].x = width/2 + scrollPtr->offset;
  648.     points[1].y = Tk_Height(tkwin) - scrollPtr->offset;
  649.     points[2].x = width + scrollPtr->offset;
  650.     points[2].y = points[0].y;
  651.     Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
  652.         points, 3, scrollPtr->borderWidth, relief);
  653.     } else {
  654.     points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
  655.         - scrollPtr->offset;
  656.     points[0].y = scrollPtr->offset - 1;
  657.     points[1].x = points[0].x;
  658.     points[1].y = width + scrollPtr->offset;
  659.     points[2].x = Tk_Width(tkwin) - scrollPtr->offset;
  660.     points[2].y = width/2 + scrollPtr->offset;
  661.     Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
  662.         points, 3, scrollPtr->borderWidth, relief);
  663.     }
  664.  
  665.     /*
  666.      * Display the slider.
  667.      */
  668.  
  669.     if (scrollPtr->mouseField == SLIDER) {
  670.     border = scrollPtr->activeBorder;
  671.     relief = scrollPtr->pressField == SLIDER ? TK_RELIEF_SUNKEN
  672.         : TK_RELIEF_RAISED;
  673.     } else {
  674.     border = scrollPtr->fgBorder;
  675.     relief = TK_RELIEF_RAISED;
  676.     }
  677.     fieldLength = (scrollPtr->vertical ? Tk_Height(tkwin) : Tk_Width(tkwin))
  678.         - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  679.     if (fieldLength < 0) {
  680.     fieldLength = 0;
  681.     }
  682.     if (scrollPtr->vertical) {
  683.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
  684.         1 + scrollPtr->offset, scrollPtr->sliderFirst,
  685.         width-2, scrollPtr->sliderLast - scrollPtr->sliderFirst,
  686.         scrollPtr->borderWidth, relief);
  687.     } else {
  688.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
  689.         scrollPtr->sliderFirst, 1 + scrollPtr->offset,
  690.         scrollPtr->sliderLast - scrollPtr->sliderFirst, width-2,
  691.         scrollPtr->borderWidth, relief);
  692.     }
  693.  
  694.     /*
  695.      * Copy the information from the off-screen pixmap onto the screen,
  696.      * then delete the pixmap.
  697.      */
  698.  
  699.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  700.     scrollPtr->copyGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  701.     XFreePixmap(Tk_Display(tkwin), pixmap);
  702.  
  703.     done:
  704.     scrollPtr->flags &= ~REDRAW_PENDING;
  705. }
  706.  
  707. /*
  708.  *--------------------------------------------------------------
  709.  *
  710.  * ScrollbarEventProc --
  711.  *
  712.  *    This procedure is invoked by the Tk dispatcher for various
  713.  *    events on scrollbars.
  714.  *
  715.  * Results:
  716.  *    None.
  717.  *
  718.  * Side effects:
  719.  *    When the window gets deleted, internal structures get
  720.  *    cleaned up.  When it gets exposed, it is redisplayed.
  721.  *
  722.  *--------------------------------------------------------------
  723.  */
  724.  
  725. static void
  726. ScrollbarEventProc(clientData, eventPtr)
  727.     ClientData clientData;    /* Information about window. */
  728.     XEvent *eventPtr;        /* Information about event. */
  729. {
  730.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  731.  
  732.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  733.     EventuallyRedraw(scrollPtr);
  734.     } else if (eventPtr->type == DestroyNotify) {
  735.     Tcl_DeleteCommand(scrollPtr->interp, Tk_PathName(scrollPtr->tkwin));
  736.     scrollPtr->tkwin = NULL;
  737.     if (scrollPtr->flags & REDRAW_PENDING) {
  738.         Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
  739.     }
  740.     Tk_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar);
  741.     } else if (eventPtr->type == ConfigureNotify) {
  742.     ComputeScrollbarGeometry(scrollPtr);
  743.     }
  744. }
  745.  
  746. /*
  747.  *----------------------------------------------------------------------
  748.  *
  749.  * ComputeScrollbarGeometry --
  750.  *
  751.  *    After changes in a scrollbar's size or configuration, this
  752.  *    procedure recomputes various geometry information used in
  753.  *    displaying the scrollbar.
  754.  *
  755.  * Results:
  756.  *    None.
  757.  *
  758.  * Side effects:
  759.  *    The scrollbar will be displayed differently.
  760.  *
  761.  *----------------------------------------------------------------------
  762.  */
  763.  
  764. static void
  765. ComputeScrollbarGeometry(scrollPtr)
  766.     register Scrollbar *scrollPtr;    /* Scrollbar whose geometry may
  767.                      * have changed. */
  768. {
  769.     int width, fieldLength;
  770.  
  771.     if (scrollPtr->relief == TK_RELIEF_FLAT) {
  772.     scrollPtr->offset = 0;
  773.     } else {
  774.     scrollPtr->offset = scrollPtr->borderWidth;
  775.     }
  776.     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
  777.         : Tk_Height(scrollPtr->tkwin);
  778.     scrollPtr->arrowLength =
  779.         (((width - 2*scrollPtr->offset)*173) + 100) / 200;
  780.     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  781.         : Tk_Width(scrollPtr->tkwin))
  782.         - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  783.     if (fieldLength < 0) {
  784.     fieldLength = 0;
  785.     }
  786.     if (scrollPtr->totalUnits <= 0) {
  787.     scrollPtr->sliderFirst = 0;
  788.     scrollPtr->sliderLast = fieldLength;
  789.     } else {
  790.     scrollPtr->sliderFirst = (fieldLength*scrollPtr->firstUnit
  791.         + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
  792.     scrollPtr->sliderLast = (fieldLength*(scrollPtr->lastUnit+1)
  793.         + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
  794.  
  795.     /*
  796.      * Adjust the slider so that some piece of it is always
  797.      * displayed in the scrollbar and so that it has at least
  798.      * a minimal width (so it can be grabbed with the mouse).
  799.      */
  800.  
  801.     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  802.         scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  803.     }
  804.     if (scrollPtr->sliderFirst < 0) {
  805.         scrollPtr->sliderFirst = 0;
  806.     }
  807.     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
  808.         + 2*scrollPtr->borderWidth)) {
  809.         scrollPtr->sliderLast = scrollPtr->sliderFirst
  810.             + 2*scrollPtr->borderWidth;
  811.     }
  812.     if (scrollPtr->sliderLast > fieldLength) {
  813.         scrollPtr->sliderLast = fieldLength;
  814.     }
  815.     }
  816.     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->offset;
  817.     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->offset;
  818.  
  819.     /*
  820.      * Register the desired geometry for the window (leave enough space
  821.      * for the two arrows plus a minimum-size slider, plus border around
  822.      * the whole window, if any).  Then arrange for the window to be
  823.      * redisplayed.
  824.      */
  825.  
  826.     if (scrollPtr->vertical) {
  827.     Tk_GeometryRequest(scrollPtr->tkwin,
  828.         scrollPtr->width + 2*scrollPtr->offset,
  829.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  830.         + scrollPtr->offset));
  831.     } else {
  832.     Tk_GeometryRequest(scrollPtr->tkwin,
  833.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  834.         + scrollPtr->offset), scrollPtr->width + 2*scrollPtr->offset);
  835.     }
  836.     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->borderWidth);
  837.  
  838. }
  839.  
  840. /*
  841.  *--------------------------------------------------------------
  842.  *
  843.  * ScrollbarPosition --
  844.  *
  845.  *    Determine the scrollbar element corresponding to a
  846.  *    given position.
  847.  *
  848.  * Results:
  849.  *    One of TOP_ARROW, TOP_GAP, etc., indicating which element
  850.  *    of the scrollbar covers the position given by (x, y).  If
  851.  *    (x,y) is outside the scrollbar entirely, then OUTSIDE is
  852.  *    returned.
  853.  *
  854.  * Side effects:
  855.  *    None.
  856.  *
  857.  *--------------------------------------------------------------
  858.  */
  859.  
  860. static int
  861. ScrollbarPosition(scrollPtr, x, y)
  862.     register Scrollbar *scrollPtr;    /* Scrollbar widget record. */
  863.     int x, y;                /* Coordinates within scrollPtr's
  864.                      * window. */
  865. {
  866.     int length, width, tmp;
  867.  
  868.     if (scrollPtr->vertical) {
  869.     length = Tk_Height(scrollPtr->tkwin);
  870.     width = Tk_Width(scrollPtr->tkwin);
  871.     } else {
  872.     tmp = x;
  873.     x = y;
  874.     y = tmp;
  875.     length = Tk_Width(scrollPtr->tkwin);
  876.     width = Tk_Height(scrollPtr->tkwin);
  877.     }
  878.  
  879.     if ((x < 0) || (x > width) || (y < 0)) {
  880.     return OUTSIDE;
  881.     }
  882.  
  883.     /*
  884.      * All of the calculations in this procedure mirror those in
  885.      * DisplayScrollbar.  Be sure to keep the two consistent.
  886.      */
  887.  
  888.     if (y < (scrollPtr->offset + scrollPtr->arrowLength)) {
  889.     return TOP_ARROW;
  890.     }
  891.     if (y < scrollPtr->sliderFirst) {
  892.     return TOP_GAP;
  893.     }
  894.     if (y < scrollPtr->sliderLast) {
  895.     return SLIDER;
  896.     }
  897.     if (y >= (length - (scrollPtr->arrowLength + scrollPtr->offset))) {
  898.     return BOTTOM_ARROW;
  899.     }
  900.     return BOTTOM_GAP;
  901. }
  902.  
  903. /*
  904.  *--------------------------------------------------------------
  905.  *
  906.  * ScrollbarMouseProc --
  907.  *
  908.  *    This procedure is called back by Tk in response to
  909.  *    mouse events such as window entry, window exit, mouse
  910.  *    motion, and button presses.
  911.  *
  912.  * Results:
  913.  *    None.
  914.  *
  915.  * Side effects:
  916.  *    This procedure implements the "feel" of the scrollbar
  917.  *    by issuing scrolling commands in response to button presses
  918.  *    and mouse motion.
  919.  *
  920.  *--------------------------------------------------------------
  921.  */
  922.  
  923. static void
  924. ScrollbarMouseProc(clientData, eventPtr)
  925.     ClientData clientData;        /* Information about window. */
  926.     register XEvent *eventPtr;        /* Information about event. */
  927. {
  928.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  929.  
  930.     Tk_Preserve((ClientData) scrollPtr);
  931.     if (eventPtr->type == EnterNotify) {
  932.     if (scrollPtr->pressField == -1) {
  933.         ScrollbarNewField(scrollPtr,
  934.             ScrollbarPosition(scrollPtr, eventPtr->xcrossing.x,
  935.             eventPtr->xcrossing.y));
  936.     }
  937.     } else if (eventPtr->type == LeaveNotify) {
  938.     if (scrollPtr->pressField == -1) {
  939.         ScrollbarNewField(scrollPtr, OUTSIDE);
  940.     }
  941.     } else if (eventPtr->type == MotionNotify) {
  942.     if (scrollPtr->pressField == SLIDER) {
  943.         int delta, length, newFirst;
  944.  
  945.         if (scrollPtr->vertical) {
  946.         delta = eventPtr->xmotion.y - scrollPtr->pressPos;
  947.         length = Tk_Height(scrollPtr->tkwin)
  948.             - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  949.         } else {
  950.         delta = eventPtr->xmotion.x - scrollPtr->pressPos;
  951.         length = Tk_Width(scrollPtr->tkwin)
  952.             - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  953.         }
  954.  
  955.         /*
  956.          * Do the division with positive numbers to avoid
  957.          * differences in negative-number truncation on different
  958.          * machines.
  959.          */
  960.  
  961.         if (delta >= 0) {
  962.         newFirst = scrollPtr->pressFirstUnit
  963.             + ((delta * scrollPtr->totalUnits) + (length/2))
  964.             / length;
  965.         } else {
  966.         newFirst = scrollPtr->pressFirstUnit
  967.             - (((-delta) * scrollPtr->totalUnits) + (length/2))
  968.             / length;
  969.         }
  970.         ScrollCmd(scrollPtr, newFirst);
  971.     } else if (scrollPtr->pressField == -1) {
  972.         ScrollbarNewField(scrollPtr,
  973.             ScrollbarPosition(scrollPtr, eventPtr->xmotion.x,
  974.             eventPtr->xmotion.y));
  975.     }
  976.     } else if ((eventPtr->type == ButtonPress)
  977.         && (eventPtr->xbutton.state == 0)) {
  978.     scrollPtr->pressField = scrollPtr->mouseField;
  979.     if (scrollPtr->pressField != SLIDER) {
  980.         scrollPtr->autoRepeat = Tk_CreateTimerHandler(
  981.             scrollPtr->repeatDelay,
  982.             ScrollbarTimerProc, (ClientData) scrollPtr);
  983.     }
  984.     if (scrollPtr->vertical) {
  985.         scrollPtr->pressPos = eventPtr->xbutton.y;
  986.     } else {
  987.         scrollPtr->pressPos = eventPtr->xbutton.x;
  988.     }
  989.     scrollPtr->pressFirstUnit = scrollPtr->firstUnit;
  990.     if (scrollPtr->pressFirstUnit <= -scrollPtr->windowUnits) {
  991.         scrollPtr->pressFirstUnit = 1-scrollPtr->windowUnits;
  992.     }
  993.     if (scrollPtr->pressFirstUnit >= scrollPtr->totalUnits) {
  994.         scrollPtr->pressFirstUnit = scrollPtr->totalUnits-1;
  995.     }
  996.     EventuallyRedraw(scrollPtr);
  997.     } else if (eventPtr->type == ButtonRelease) {
  998.     if (scrollPtr->pressField == scrollPtr->mouseField) {
  999.         switch (scrollPtr->pressField) {
  1000.         case TOP_ARROW:
  1001.             ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
  1002.             break;
  1003.         case TOP_GAP:
  1004.             if (scrollPtr->windowUnits <= 1) {
  1005.             ScrollCmd(scrollPtr, scrollPtr->firstUnit - 1);
  1006.             } else {
  1007.             ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1008.                 - (scrollPtr->windowUnits-1));
  1009.             }
  1010.             break;
  1011.         case BOTTOM_GAP: {
  1012.             if (scrollPtr->windowUnits <= 1) {
  1013.             ScrollCmd(scrollPtr, scrollPtr->firstUnit + 1);
  1014.             } else {
  1015.             ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1016.                 + (scrollPtr->windowUnits-1));
  1017.             }
  1018.             break;
  1019.         }
  1020.         case BOTTOM_ARROW:
  1021.             ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
  1022.             break;
  1023.         }
  1024.     }
  1025.     if (scrollPtr->autoRepeat != NULL) {
  1026.         Tk_DeleteTimerHandler(scrollPtr->autoRepeat);
  1027.         scrollPtr->autoRepeat = NULL;
  1028.     }
  1029.     EventuallyRedraw(scrollPtr);
  1030.     scrollPtr->pressField = -1;
  1031.     ScrollbarNewField(scrollPtr,
  1032.         ScrollbarPosition(scrollPtr, eventPtr->xbutton.x,
  1033.         eventPtr->xbutton.y));
  1034.     }
  1035.     Tk_Release((ClientData) scrollPtr);
  1036. }
  1037.  
  1038. /*
  1039.  *--------------------------------------------------------------
  1040.  *
  1041.  * ScrollCmd --
  1042.  *
  1043.  *    This procedure takes care of invoking a scrolling Tcl
  1044.  *    command and reporting any error that occurs in it.
  1045.  *
  1046.  * Results:
  1047.  *    None.
  1048.  *
  1049.  * Side effects:
  1050.  *    A Tcl command is invoked, and an additional error-processing
  1051.  *    command may also be invoked.
  1052.  *
  1053.  *--------------------------------------------------------------
  1054.  */
  1055.  
  1056. static void
  1057. ScrollCmd(scrollPtr, unit)
  1058.     register Scrollbar *scrollPtr;    /* Scrollbar from which to issue
  1059.                      * command. */
  1060.     int unit;                /* Unit position within thing being
  1061.                      * being displayed that should appear
  1062.                      * at top or right of screen. */
  1063. {
  1064.     char string[20];
  1065.     int result;
  1066.  
  1067.     if ((unit == scrollPtr->firstUnit) || (scrollPtr->command == NULL)) {
  1068.     return;
  1069.     }
  1070.     sprintf(string, " %d", unit);
  1071.     result = Tcl_VarEval(scrollPtr->interp, scrollPtr->command, string,
  1072.         (char *) NULL);
  1073.     if (result != TCL_OK) {
  1074.     TkBindError(scrollPtr->interp);
  1075.     }
  1076. }
  1077.  
  1078. /*
  1079.  *--------------------------------------------------------------
  1080.  *
  1081.  * EventuallyRedraw --
  1082.  *
  1083.  *    Arrange for one or more of the fields of a scrollbar
  1084.  *    to be redrawn.
  1085.  *
  1086.  * Results:
  1087.  *    None.
  1088.  *
  1089.  * Side effects:
  1090.  *    None.
  1091.  *
  1092.  *--------------------------------------------------------------
  1093.  */
  1094.  
  1095. static void
  1096. EventuallyRedraw(scrollPtr)
  1097.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1098. {
  1099.     if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
  1100.     return;
  1101.     }
  1102.     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
  1103.     Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
  1104.     scrollPtr->flags |= REDRAW_PENDING;
  1105.     }
  1106. }
  1107.  
  1108. /*
  1109.  *--------------------------------------------------------------
  1110.  *
  1111.  * ScrollbarNewField --
  1112.  *
  1113.  *    This procedure is called to declare that the mouse is in
  1114.  *    a particular field of the scrollbar (e.g. top arrow), so
  1115.  *    that the field can be highlighed and the previous field
  1116.  *    can be returned to normal display.
  1117.  *
  1118.  * Results:
  1119.  *    None.
  1120.  *
  1121.  * Side effects:
  1122.  *    Fields may be redisplayed.
  1123.  *
  1124.  *--------------------------------------------------------------
  1125.  */
  1126.  
  1127. static void
  1128. ScrollbarNewField(scrollPtr, field)
  1129.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1130.     int field;                /* Identifies field under mouse,
  1131.                      * e.g. TOP_ARROW. */
  1132. {
  1133.     if (field == scrollPtr->mouseField) {
  1134.     return;
  1135.     }
  1136.     EventuallyRedraw(scrollPtr);
  1137.     scrollPtr->mouseField = field;
  1138. }
  1139.  
  1140. /*
  1141.  *--------------------------------------------------------------
  1142.  *
  1143.  * ScrollbarTimerProc --
  1144.  *
  1145.  *    This procedure is invoked as a Tk timer handler for actions
  1146.  *    that auto-repeat (mouse presses in an arrow or gap).  It
  1147.  *    performs the auto-repeat action.
  1148.  *
  1149.  * Results:
  1150.  *    None.
  1151.  *
  1152.  * Side effects:
  1153.  *    Whatever action corresponds to the current mouse button
  1154.  *    is repeated, and this procedure is rescheduled to execute
  1155.  *    again later.
  1156.  *
  1157.  *--------------------------------------------------------------
  1158.  */
  1159.  
  1160. static void
  1161. ScrollbarTimerProc(clientData)
  1162.     ClientData clientData;    /* Information about widget. */
  1163. {
  1164.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  1165.  
  1166.     Tk_Preserve((ClientData) scrollPtr);
  1167.     switch(scrollPtr->pressField) {
  1168.     case TOP_ARROW:
  1169.         ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
  1170.         break;
  1171.     case TOP_GAP:
  1172.         ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1173.             - (scrollPtr->windowUnits-1));
  1174.         break;
  1175.     case BOTTOM_GAP: {
  1176.         ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1177.             + (scrollPtr->windowUnits-1));
  1178.         break;
  1179.     }
  1180.     case BOTTOM_ARROW:
  1181.         ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
  1182.         break;
  1183.     }
  1184.     if (scrollPtr->tkwin != NULL) {
  1185.     scrollPtr->autoRepeat = Tk_CreateTimerHandler(
  1186.         scrollPtr->repeatInterval, ScrollbarTimerProc,
  1187.         (ClientData) scrollPtr);
  1188.     }
  1189.     Tk_Release((ClientData) scrollPtr);
  1190. }
  1191.